﻿namespace EFS.RC.CC.Applications.Remote
{
	/// <summary>
	/// Remote application
	/// </summary>
	internal abstract class Messaging : EFS.Common.Async.AsyncRoot, EFS.RC.CC.SAL.Writers.IWriter
	{
		#region Composition

		// sync access
		private object syncAccess = new object();

		// Call -in for application app
		private Microsoft.Rtc.Collaboration.InstantMessagingCall applicationInstantMessagingCall = null;

		// Flow -in for application app
		private Microsoft.Rtc.Collaboration.InstantMessagingFlow applicationInstantMessagingFlow = null;

		/// <summary>
		/// Status
		/// </summary>
		public enum ApplicationStatus { Idle, Started, Terminated }

		// Status
		private ApplicationStatus status = ApplicationStatus.Idle;

		#endregion

		#region Delegates

		/// <summary>
		/// Asynk terminate 
		/// </summary>
		public EFS.Common.Async.AsyncDelegate<EFS.RC.CC.Applications.Remote.Messaging> AsinkTerminatedDelegate { get; private set; }

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="applicationPresence"></param>
		public Messaging(Microsoft.Rtc.Collaboration.InstantMessagingCall applicationInstantMessagingCall)
		{
			// check
			EFS.Common.Verify.Reference(applicationInstantMessagingCall, "applicationInstantMessagingCall");

			// delegate
			AsinkTerminatedDelegate = new EFS.Common.Async.AsyncDelegate<Messaging>(this);

			// set internals
			this.applicationInstantMessagingCall = applicationInstantMessagingCall;

			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("Messaging", this);
		}

		#endregion

		#region Usage

		/// <summary>
		/// Send data message into the channel
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		public System.IAsyncResult BeginDataSend(System.Net.Mime.ContentType contentType, byte[] message)
		{
			// check
			EFS.Common.Verify.Reference(message, "message");

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// test status
				if (null == applicationInstantMessagingFlow)
				{
					return null;
				}

				// begin send context data
				return applicationInstantMessagingFlow.BeginSendInstantMessage(
					contentType,
					message,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndSendInstantMessageData, applicationInstantMessagingFlow));
			}
		}

		/// <summary>
		/// Send data message into the channel
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		public System.IAsyncResult BeginDataSend(System.String message)
		{
			// check
			EFS.Common.Verify.Reference(message, "message");

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// test status
				if (null == applicationInstantMessagingFlow)
				{
					return null;
				}

				// begin send context data
				return applicationInstantMessagingFlow.BeginSendInstantMessage(
					message,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndSendInstantMessageData, applicationInstantMessagingFlow));
			}
		}

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Applications.Remote.Messaging {0} {1}",
				applicationInstantMessagingCall.Conversation.Endpoint.OwnerUri, applicationInstantMessagingCall.RemoteEndpoint.Uri);
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// start
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		#endregion

		#region Startup

		/// <summary>
		/// Session startup
		/// </summary>
		private void Startup()
		{
			// commented for performance ~ EFS.EventLog.Log.LogInfo("Startup", this);

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (status != ApplicationStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle Messaging can be started");
				}

				// started
				status = ApplicationStatus.Started;
			}

			// set context app as this
			applicationInstantMessagingCall.Conversation.ApplicationContext = this;

			// flow event
			applicationInstantMessagingCall.InstantMessagingFlowConfigurationRequested += OnInstantMessagingFlowConfigurationRequested;
			applicationInstantMessagingCall.Conversation.StateChanged += OnConversationStateChanged;

			// accept call
			applicationInstantMessagingCall.BeginAccept(EndAcceptApplicationCall, null);
		}

		/// <summary>
		/// Flow configuration request
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnInstantMessagingFlowConfigurationRequested(object sender, Microsoft.Rtc.Collaboration.InstantMessagingFlowConfigurationRequestedEventArgs e)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("OnInstantMessagingFlowConfigurationRequested", this);

			try
			{
				// lock local acess
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
				lock (syncAccess)
				{
					// get flow
					applicationInstantMessagingFlow = e.Flow;

					// register events
					applicationInstantMessagingFlow.MessageReceived += OnMessageReceived;
					applicationInstantMessagingFlow.StateChanged += OnFlowStateChanged;

					// remove 
					applicationInstantMessagingCall.InstantMessagingFlowConfigurationRequested -= OnInstantMessagingFlowConfigurationRequested;
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}		
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				// commented for performance ~ EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				//shutdown
				BeginShutdown();
			}
		}

		/// <summary>
		/// Send monitored applications list
		/// </summary>
		protected abstract void OnEstablished();

		/// <summary>
		/// Accept application call
		/// </summary>
		/// <param name="ar"></param>
		private void EndAcceptApplicationCall(System.IAsyncResult ar)
		{
			// commented for performance ~ EFS.EventLog.Log.LogInfo("EndAcceptApplicationCall", this);

			try
			{
				// try 
				applicationInstantMessagingCall.EndAccept(ar);

				// established
				OnEstablished();
			}
			catch (System.Exception ex)
			{
				EFS.EventLog.Log.LogException(ex, this);

				// shut it down
				BeginShutdown();
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(null, "OnException", ex, this);

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Message received
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected abstract void OnMessageReceived(System.Object sender, Microsoft.Rtc.Collaboration.InstantMessageReceivedEventArgs e);

		/// <summary>
		/// Changes to application conversation
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnConversationStateChanged(object sender, Microsoft.Rtc.Signaling.StateChangedEventArgs<Microsoft.Rtc.Collaboration.ConversationState> args)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo(System.String.Format("OnConversationStateChanged {0}", args.State), this);

			try
			{
				// manage
				switch (args.State)
				{
					case Microsoft.Rtc.Collaboration.ConversationState.Terminating:
						{
							// begin shut down
							BeginShutdown();
							break;
						}
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}	
		}

		/// <summary>
		/// Changes to channel
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnFlowStateChanged(object sender, Microsoft.Rtc.Collaboration.MediaFlowStateChangedEventArgs e)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo(System.String.Format("OnFlowStateChanged {0}", e.State), this);

			try
			{
				// channel state changes
				if (e.State == Microsoft.Rtc.Collaboration.MediaFlowState.Terminated)
				{
					// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
					lock (syncAccess)
					{
						// remoev flow
						if (null != applicationInstantMessagingFlow)
						{
							applicationInstantMessagingFlow.MessageReceived -= OnMessageReceived;
							applicationInstantMessagingFlow.StateChanged -= OnFlowStateChanged;
							applicationInstantMessagingFlow = null;
						}
					}

					// terminated, begin shut down
					BeginShutdown();
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}	
		}

		/// End data send
		/// </summary>
		/// <param name="result"></param>
		internal void EndSendInstantMessageData(System.IAsyncResult ar)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("EndSendInstantMessageData", this);

			try
			{
				// get state delaget
				EFS.Common.Async.AsyncResult.StateDelegate stateDelegate = ar.AsyncState as EFS.Common.Async.AsyncResult.StateDelegate;
				EFS.Common.Verify.Reference(stateDelegate, "stateDelegate");

				// check
				Microsoft.Rtc.Collaboration.InstantMessagingFlow instantMessagingFlow = (stateDelegate.PassTrough as Microsoft.Rtc.Collaboration.InstantMessagingFlow);
				EFS.Common.Verify.Reference(instantMessagingFlow, "instantMessagingFlow");

				// check
				instantMessagingFlow.EndSendInstantMessage(ar);

				// log
				// commented for performance ~ EFS.EventLog.Log.LogInfo("EndSendInstantMessageData SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// shutdown
				BeginShutdown();
			}
		}

		/// <summary>
		/// Write
		/// </summary>
		/// <param name="message"></param>
		public void Write(EFS.RC.CC.SAL.Loggers.Messages.Message message)
		{
			// check message
			EFS.Common.Verify.Reference(message, "message");

			// send message
			System.Text.StringBuilder sb = new System.Text.StringBuilder();

			// when
			sb.AppendLine(message.Occurred.ToString());

			// conversation
			if (null != message.ConversationGuid)
			{
				sb.AppendLine(System.String.Format("Conversation Guid {0}", message.ConversationGuid));
			}

			// al the rest
			sb.AppendLine(message.Context);
			sb.AppendLine(message.Method);
			sb.AppendLine(message.Detail);

			// send
			BeginDataSend(sb.ToString());
		}

		/// <summary>
		/// Register for terminate
		/// </summary>
		/// <param name="terminateDelegate"></param>
		public void RegisterTerminate<T>(EFS.Common.Async.AsyncDelegate<T>.Delegate terminateDelegate) where T : EFS.RC.CC.SAL.Writers.IWriter  
		{
 			// register
			this.AsinkTerminatedDelegate.Register(terminateDelegate as EFS.Common.Async.AsyncDelegate<EFS.RC.CC.Applications.Remote.Messaging>.Delegate);
		}

		#endregion // Runtime

		#region Shutdown

		/// <summary>
		/// Shut down
		/// </summary>
		public void Shutdown()
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check already terminated
				if (status != ApplicationStatus.Terminated)
				{
					// set terinated
					status = ApplicationStatus.Terminated;

					// notify
					AsinkTerminatedDelegate.Start();
				}
				else
				{
					// already terminated
					return;
				}
			}

			// make termination
			EFS.RC.CC.Tools.Functions.Termination termination = new EFS.RC.CC.Tools.Functions.Termination(this);
			
			// terminate all
			termination.Terminate(applicationInstantMessagingCall, EndTerminateInstantMessagingCall);
		}

		/// End conversation
		/// </summary>
		/// <param name="ar"></param>
		private void EndTerminateInstantMessagingCall(System.IAsyncResult ar)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("EndTerminateCustomerConversation", this);

			try
			{
				// remove delegates and context 
				applicationInstantMessagingCall.Conversation.StateChanged -= OnConversationStateChanged;
				applicationInstantMessagingCall.Conversation.ApplicationContext = null;

				// end terminate
				applicationInstantMessagingCall.EndTerminate(ar);
			}
			catch (System.Exception ex)
			{
				// handle exception
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			// commented for performance ~ EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				// commented for performance ~ EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion
	}
}

